﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using LastoRevival.Website.Models;
using AttributeRouting.Web.Mvc;
using LastoRevival.Service.Contracts.Models;
using LastoRevival.Service.Implementations.Exceptions;
using mdryden.Core.Localization;
using mdryden.Core.Data;

namespace LastoRevival.Website.Controllers
{
	public class UserController : LastoControllerBase
	{
		private void VerifyEditPermissions(UserProfileDetails currentUser, int userID)
		{
			if (!CanEdit(currentUser, userID))
			{
				throw new NoAccessException();
			}
		}

		private bool CanEdit(UserProfileDetails currentUser, int userID)
		{
			// must either be an admin, or the user himself
			if (currentUser.UserID == userID)
			{
				return true;
			}

			if (currentUser.IsAdministrator)
			{
				return true;
			}

			// nope
			return false;
		}


		[GET("account/login")]
		public ActionResult Login(bool? justVerified)
		{
			ViewBag.JustVerified = justVerified;
			
			return View();
		}

		[POST("account/login")]
		public ActionResult Login(LogOnModel model, string returnUrl)
		{
			if (ModelState.IsValid)
			{
				var result = Service.Login(LoginState.Token, model.UserName, model.Password, model.RememberMe);

				switch (result.Status)
				{
					case LoginResult.Success:

						// log the user in
						LoginState.Login(result.LoginToken.Value, result.AuthenticatedUntil.Value);

						if (Url.IsLocalUrl(returnUrl))
						{
							return Redirect(returnUrl);
						}
						else
						{
							return RedirectToAction("Index", "Forum");
						}

					case LoginResult.PasswordError:
						ModelState.AddModelError("PasswordError", LanguageLoader.GetWord("PasswordErrorMessage"));
						break;

					case LoginResult.UserNotFound:
						ModelState.AddModelError("UserError", LanguageLoader.GetWord("UsernameErrorMessage"));
						break;

					case LoginResult.NotVerified:
						return RedirectToAction("VerifyForm", "User", new { username = model.UserName });
				}
			}

			// If we got this far, something failed, redisplay form
			return View(model);
		}

		[GET("account/logout")]
		public ActionResult Logout()
		{
			Service.Logout(LoginState.Token);

			LoginState.Logout();

			return RedirectToAction("Index", "Forum");
		}

		[GET("account/register")]
		public ActionResult Register()
		{
			return View("Register", new CreateUserModel());
		}

		[POST("account/register")]
		public ActionResult Register(CreateUserModel model)
		{
			if (ModelState.IsValid)
			{
				var createStatus = Service.CreateUser(LoginState.Token, model);

				switch (createStatus.Result)
				{
					case CreateUserResult.Success:
						{
							// add default view settings for this user
							SiteDataProvider.InsertUserSettings(createStatus.NewUserID, SiteUserSettings.CreateAnonymous());

							// check what the verification type is
							switch (createStatus.VerificationType)
							{
								case UserVerificationType.None:
									// user is already verified, log in and redirect
									// try to log in
									return Login(new LogOnModel { UserName = model.UserName, Password = model.Password }, string.Empty);

								case UserVerificationType.Email:
									// send verification email and put user on the form
									return RedirectToAction("SendVerification", "User", new { username = model.UserName });

								case UserVerificationType.Manual:
									// show user the wait message
									return ForumMessage(LanguageLoader.GetWord("AwaitingAdminApproval"));

								default:
									throw new NotSupportedException();
							}
						}

					case CreateUserResult.DuplicateUsername:
						{
							ModelState.AddModelError("", LanguageLoader.GetWord("DuplicateUsernameError"));
							break;
						}

					default:
						{
							ModelState.AddModelError("", LanguageLoader.GetWord("RegistrationDBError"));
							break;
						}

				}
			}

			// we didn't get redirected, so we're headed back to display the error(s)
			return View(model);

		}

		[GET("user/{username}")]
		public ActionResult ViewUser(string username)
		{

			var user = Service.GetPublicProfile(LoginState.Token, username);

			return View(user);

		}

		[GET("user/{username}/avatar")]
		public FileResult Avatar(string username)
		{
			return File("~/Images/default-avatar.png", "Image/PNG");
		}

		[GET("user/{username}/settings")]
		public ActionResult EditSettingsForm(string username)
		{

			var user = Service.GetProfileForEdit(LoginState.Token, username);

			// this is a duplicate check, but in theory we can't be assured the service is doing the check
			VerifyEditPermissions(UserProfile.Details, user.UserID);

			var model = SiteDataProvider.GetUserSettingsForEdit(user.UserID);

			model.Username = user.Username;

			return View("EditSettings", model);

		}

		[POST("user/settings")]
		public ActionResult EditSettings(EditSiteUserSettingsModel model)
		{

			if (!ModelState.IsValid)
			{
				// bail
				return View(model);
			}

			VerifyEditPermissions(UserProfile.Details, model.UserID);

			var username = Service.GetUsername(LoginState.Token, model.UserID);

			SiteDataProvider.UpdateUserSettings(model);

			return RedirectToAction("ViewUser", "User", new { username = username });


		}

		[GET("user/{username}/profile")]
		public ActionResult EditProfileForm(string username)
		{

			var profile = Service.GetProfileForEdit(LoginState.Token, username);

			return View("EditProfile", profile);

		}

		[POST("user/{username}/profile")]
		public ActionResult EditProfile(EditProfileModel model, string username)
		{

			if (!ModelState.IsValid)
			{
				// fix the breadcrumb
				var profile = Service.GetProfileForEdit(LoginState.Token, username);
				model.Username = profile.Username;
				// bail
				return View(model);
			}

			Service.UpdateProfile(LoginState.Token, model);
			
			return RedirectToAction("ViewUser", "User", new { username = username });
		}

		[GET("users")]
		public ActionResult List()
		{
			throw new NotImplementedException();
		}

		[GET("verify/{username}")]
		public ActionResult VerifyForm(string username, string code, bool? emailSent)
		{
			if (string.IsNullOrEmpty(username))
			{
				throw new ArgumentNullException("username");
			}

			var model = new VerifyUserModel { Username = username, Code = code };

			if (!string.IsNullOrEmpty(code))
			{
				// don't make the user deal with the form
				return Verify(model);
			}

			// no code provided, show the form
			ViewBag.EmailSent = emailSent;

			return View("Verify", model);
		}

		[POST("verify")]
		public ActionResult Verify(VerifyUserModel model)
		{
			if (!ModelState.IsValid)
			{
				return View("Verify", model);
			}

			var verified = Service.VerifyUser(LoginState.Token, model);

			if (!verified)
			{
				ModelState.AddModelError("Code", LanguageLoader.GetWord("User_VerificationCodeInvalidError"));
				return View("Verify", model);
			}

			// verified
			return RedirectToAction("Login", "User", new { justVerified = true });
		}

		[GET("sendverify/{username}")]
		public ActionResult SendVerification(string username)
		{

			try
			{
				var builder = new UriBuilder(Request.Url.AbsoluteUri);
				builder.Path = Url.Action("VerifyForm", "User");
				builder.Query = "code=[0]";

				var link = builder.Uri.ToString();

				var bodyFormat = string.Format(LanguageLoader.GetWord("VerificationEmailFormat"), link);
				bodyFormat = bodyFormat.Replace("[0]", "{0}");

				Service.SendVerificationEmail(LoginState.Token, username, bodyFormat);
			}
			catch (UserIsVerifiedException)
			{
				// user was already verified....
				return ForumError(LanguageLoader.GetWord("User_AlreadyVerifiedError"));
			}
			catch (InvalidRowCountException)
			{
				return ForumError(LanguageLoader.GetWord("User_NotFoundError"));
			}

			// done
			return RedirectToAction("VerifyForm", "User", new { username = username, emailSent = true });

		}
	}
}
